package com.zhang.service.impl;

import com.zhang.client.UserFeignClient;
import com.zhang.constant.RedisConst;
import com.zhang.entity.*;
import com.zhang.mapper.SeckillProductMapper;
import com.zhang.result.RetVal;
import com.zhang.result.RetValCodeEnum;
import com.zhang.service.SeckillProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhang.util.MD5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zbw
 * @since 2022-04-26
 */
@Service
public class SeckillProductServiceImpl extends ServiceImpl<SeckillProductMapper, SeckillProduct> implements SeckillProductService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserFeignClient userFeignClient;
    @Override
    public SeckillProduct getSeckillById(@PathVariable Long skuId) {
        SeckillProduct seckillProduct = (SeckillProduct) redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).get(skuId.toString());
        return seckillProduct;
    }
    //开始处理预下单逻辑
    @Override
    public void prepareSecKill(UserSeckillSkuInfo userSeckillSkuInfo) {
        Long skuId = userSeckillSkuInfo.getSkuId();
        String userId = userSeckillSkuInfo.getUserId();
        String state = (String) redisTemplate.opsForValue().get(RedisConst.SECKILL_STATE_PREFIX + skuId);
        //商品已经售空
        if (RedisConst.CAN_NOT_SECKILL.equals(state)){
            return;
        }
        //判断用户是否下过预订单 setIfAbsent():没有值结果为true,有值结果为false
        boolean flag=redisTemplate.opsForValue().setIfAbsent(RedisConst.PREPARE_SECKILL_USERID_SKUID+":"+userId+":"+skuId,skuId,RedisConst.PREPARE_SECKILL_LOCK_TIME, TimeUnit.SECONDS);
        if (!flag){
            return;
        }
        //校验库存是否足够 如果有库存 需要减库存  seckill:stock:24
        String redisStockSkuId = (String) redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).rightPop();
        if (StringUtils.isEmpty(redisStockSkuId)){
            //没有库存 通知其他redis节点修改秒杀状态位
            redisTemplate.convertAndSend(RedisConst.PREPARE_PUB_SUB_SECKILL,skuId+":"+RedisConst.CAN_NOT_SECKILL);
            return;
        }
        //生成一个临时订单存储到redis中 prepare:seckill:userId:order
        PrepareSeckillOrder prepareSeckillOrder = new PrepareSeckillOrder();
        prepareSeckillOrder.setUserId(userId);
        prepareSeckillOrder.setBuyNum(1);
        SeckillProduct seckillProduct = getSeckillById(skuId);
        prepareSeckillOrder.setSeckillProduct(seckillProduct);
        //生成一个订单码
        prepareSeckillOrder.setPrepareOrderCode(MD5.encrypt(userId+skuId));
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).put(userId,prepareSeckillOrder);
        //更新库存信息
        updateSecKillStockCount(skuId);
    }

    /**更新库存信息
     * 先拿到剩余库存数量，再由总数量-剩余得到锁定库存量，最后再更新到redis中
     * @param skuId
     */
    private void updateSecKillStockCount(Long skuId) {
        //剩余库存量
        Long leftStock = redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).size();
        //更新库存（数据库）频次 自定义一个规则
        if (leftStock%2==0){
            SeckillProduct redisSeckillProduct = getSeckillById(skuId);
            //锁定库存量=总商品数量-剩余库存量
            Integer totalStock = redisSeckillProduct.getNum();
            int lockStock=totalStock-Integer.parseInt(leftStock+"");
            redisSeckillProduct.setStockCount(lockStock);
            //更新redis里面的商品锁定库存量 目的是为了给用户看进度
            redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).put(skuId.toString(),redisSeckillProduct);
            //更新数据库为了持久化防止数据丢失
            baseMapper.updateById(redisSeckillProduct);
        }
    }

    //5.判断用户是否具备抢购资格
    @Override
    public RetVal hasQualified(Long skuId, String userId) {
        //如果预下单里面有用户信息 就代表该用户具备抢购资格  prepare:seckill:userId:skuId:3:24
        boolean isExist = redisTemplate.hasKey(RedisConst.PREPARE_SECKILL_USERID_SKUID + ":" + userId + ":" + skuId);
        if(isExist){
            //拿出用户的预购单信息  prepare:seckill:userId:order
            PrepareSeckillOrder prepareSeckillOrder=(PrepareSeckillOrder)redisTemplate.
                    boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
            if(prepareSeckillOrder!=null){
                //返回预购单信息与秒杀资格抢购成功的信息
                return RetVal.build(prepareSeckillOrder, RetValCodeEnum.PREPARE_SECKILL_SUCCESS);
            }
        }
        //如果用户已经购买过该商品  bought:seckill:userId:order
        Integer orderId =(Integer) redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).get(userId);
        if(orderId!=null){
            /**
             * 如果订单id不为空 代表该用户购买过该商品 此时页面不需要显示为排队中
             * SECKILL_ORDER_SUCCESS 代表抢购成功--页面显示为抢购成功
             */
            return RetVal.build(null,RetValCodeEnum.SECKILL_ORDER_SUCCESS);

        }
        //如果预购单中没有 客户继续排队
        return RetVal.build(null,RetValCodeEnum.SECKILL_RUN);
    }

    //6.返回秒杀确认页面需要的数据
    @Override
    public RetVal seckillConfirm(String userId) {
        //a.拿到收货人的地址信息 从shop-user中去查询
        List<UserAddress> userAddressList = userFeignClient.getUserAddressByUserId(userId);
        //b.用户预购单信息
        PrepareSeckillOrder prepareSeckillOrder=(PrepareSeckillOrder)redisTemplate.
                boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
        if (prepareSeckillOrder==null){
            return RetVal.fail().message("非法请求");
        }

        //c.把预购单里面的信息转换为订单详情
        SeckillProduct seckillProduct = prepareSeckillOrder.getSeckillProduct();
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setSkuId(seckillProduct.getSkuId());
        orderDetail.setSkuName(seckillProduct.getSkuName());
        orderDetail.setImgUrl(seckillProduct.getSkuDefaultImg());
        orderDetail.setSkuNum(prepareSeckillOrder.getBuyNum()+"");
        //订单的价格
        orderDetail.setOrderPrice(seckillProduct.getCostPrice());
        List<OrderDetail> orderDetailList = new ArrayList<>();
        orderDetailList.add(orderDetail);

        Map<String, Object> retMap = new HashMap<>();
        retMap.put("userAddressList",userAddressList);
        retMap.put("orderDetailList",orderDetailList);
        retMap.put("totalMoney",seckillProduct.getCostPrice());
        return RetVal.ok(retMap);
    }
}
